package com.cjkj.system.util;

import com.cjkj.common.core.constant.SleepConstants;
import com.cjkj.system.api.domain.PatientSleepStageRecord;
import com.cjkj.system.domain.vo.SleepTime;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

public class ReportUtil {
    private ReportUtil(){}

    /**
     * 获取众数
     * @param list
     * @return
     */
    public static int getMode(List<Integer> list) {
        if(list.isEmpty()) {
            return 0;
        }
        // 步骤1：计算每个元素的频率
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        for (int num : list) {
            frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1);
        }

        // 步骤2：找到最大的频率
        int maxFrequency = 0;
        for (int count : frequencyMap.values()) {
            if (count > maxFrequency) {
                maxFrequency = count;
            }
        }

        // 步骤3：收集所有具有最大频率的元素
        List<Integer> modes = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
            if (entry.getValue() == maxFrequency) {
                modes.add(entry.getKey());
            }
        }

        return modes.size() == 1 ? modes.get(0) : getMedian(list);
    }

    /**
     * 获取中位数
     * @param list
     * @return
     */
    public static int getMedian(List<Integer> list){
        if(list.isEmpty()) {
            return 0;
        }
        // 排序
        Collections.sort(list);
        // 数据个数
        int dataSize = list.size();
        // 数据个数是奇数还是偶数
        boolean isEven = dataSize % 2 == 0;

        // 获取中位数
        if(isEven) {
            return (int) Math.round((list.get(dataSize / 2) + list.get(dataSize / 2 - 1)) / 2.0);
        } else {
            return list.get(dataSize / 2);
        }
    }

    /**
     * 求一组数据的加权平均数
     * @param data
     * @return
     */
    public static double getAvg(List<Integer> data){
        if(data.isEmpty()) {
            return 0.0;
        }
        Map<Integer, Double> proportionMap = getProportion(data);
        double total = 0, totalProportion = 0;
        for (int number : data) {
            Double proportion = proportionMap.get(number);
            total += number * proportion;
            totalProportion += proportion;
        }
        return total / totalProportion;
    }

    /**
     * 获取一组数字的权重值
     * @param numbers
     * @return
     */
    public static Map<Integer, Double> getProportion(List<Integer> numbers) {
        Map<Integer, Integer> countMap = new HashMap<>();
        for (int number : numbers) {
            countMap.put(number, countMap.getOrDefault(number, 0) + 1);
        }

        int totalCount = numbers.size();
        Map<Integer, Double> proportionMap = new HashMap<>();
        for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
            double proportion = (double) entry.getValue() / totalCount;
            proportionMap.put(entry.getKey(), proportion);
        }
        return proportionMap;
    }

    /**
     * 移除一组数字中的最大最小值
     * @param list
     */
    public static void removeMaxAndMin(List<Integer> list) {
        Collections.sort(list);
        if (list.size() >= 2) {
            // 移除最大值
            list.remove(list.size() - 1);
            // 移除最小值
            list.remove(0);
        }
    }

    /**
     * 格式化浮点数
     * 四舍五入
     * @param value 小数
     * @param places 保留的小数位
     * @return 数字字符串
     */
    public static String doubleFmt(double value, int places) {
        if (places < 0) {
            places = 0;
        }
        BigDecimal bd = BigDecimal.valueOf(value);
        bd = bd.setScale(places, RoundingMode.HALF_UP);

        return bd.toString();
    }

    /**
     * 根据状态变化表计算报告
     *
     * a.计算深睡时长；
     * b.计算浅睡时长；
     * c.计算清醒时长；
     * d.计算起床时长；
     * f.计算入睡时长；
     **/
    public static SleepTime calculateSleepTime(List<PatientSleepStageRecord> statusRecords) {
        SleepTime sleepTime = new SleepTime();
        long deepSleepTime = 0L;
        long lightSleepTime = 0L;
        long awakeTime = 0L;
        long outBedTime = 0L;

        if (statusRecords != null && !statusRecords.isEmpty()) {
            Date goBedTime = new Date();
            Date lastOutBed = new Date();
            Date asleepTime = new Date();
            /*
            * 上床时间
            * 升序排列
            * */
            List<PatientSleepStageRecord> onBedStatusList = statusRecords.stream()
                    .sorted(Comparator.comparing(PatientSleepStageRecord::getAcquisitionTime))
                    // 不用筛掉离床的数据
//                    .filter(e -> !SleepConstants.SLEEP_STATUS_OUT_BED.equals(e.getSleepStage()))
                    .collect(Collectors.toList());
            if (!onBedStatusList.isEmpty()) {
                goBedTime = onBedStatusList.get(0).getAcquisitionTime();
            } else {
                goBedTime = null;
            }

            /*
                最后一次离床时间（起床时间）
                降序
            */
            List<PatientSleepStageRecord> outBedList = statusRecords.stream().
                    sorted(Comparator.comparing(PatientSleepStageRecord::getAcquisitionTime).reversed())
                    .filter(e -> SleepConstants.SLEEP_STATUS_OUT_BED.equals(e.getSleepStage()))
                    .collect(Collectors.toList());
            if (!outBedList.isEmpty()) {
                lastOutBed = outBedList.get(0).getAcquisitionTime();
            } else {
                lastOutBed = null;
            }

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(statusRecords.get(statusRecords.size() - 1).getAcquisitionTime());
            Date outBedTemp = calendar.getTime();
            if (lastOutBed == null || lastOutBed.before(outBedTemp)) {
                lastOutBed = outBedTemp;
            }

            int getUpCount = 0;
            for (int i = 1; i < statusRecords.size(); i++) {
                PatientSleepStageRecord beforeSleepStageRecord = statusRecords.get(i - 1);
                String beforeSleepStage = beforeSleepStageRecord.getSleepStage();

                PatientSleepStageRecord nowSleepStageRecord = statusRecords.get(i);
                String nowSleepStage = nowSleepStageRecord.getSleepStage();

                long duringTime = 0L;
                if (nowSleepStageRecord.getAcquisitionTime() != null && beforeSleepStage != null) {
                    duringTime = (nowSleepStageRecord.getAcquisitionTime().getTime() - beforeSleepStageRecord.getAcquisitionTime().getTime()) / 1000;
                }
                if (SleepConstants.SLEEP_STATUS_OUT_BED.equals(nowSleepStage)) {
                    getUpCount ++;
                }
//                if (!SleepConstants.SLEEP_STATUS_OUT_BED.equals(nowSleepStage)
//                        && SleepConstants.SLEEP_STATUS_OUT_BED.equals(beforeSleepStage)) {
//                    getUpCount ++;
//                }
                if (SleepConstants.SLEEP_STATUS_OUT_BED.equals(nowSleepStage)) {
                    outBedTime += duringTime;
                } else if (SleepConstants.SLEEP_STAGE_WIDE_AWAKE.equals(nowSleepStage)) {
                    awakeTime += duringTime;
                } else if (SleepConstants.SLEEP_STAGE_LIGHT_SLEEP.equals(nowSleepStage)) {
                    lightSleepTime += duringTime;
                } else if (SleepConstants.SLEEP_STAGE_DEEP_SLEEP.equals(nowSleepStage)) {
                    deepSleepTime += duringTime;
                }
            }
            sleepTime.setGetUpCount(getUpCount);
            sleepTime.setGoBedTime(goBedTime);
            sleepTime.setAsleepTime(asleepTime);
            sleepTime.setLastOutBed(lastOutBed);
        }else {
            sleepTime.setGetUpCount(0);
            sleepTime.setGoBedTime(null);
            sleepTime.setAsleepTime(null);
            sleepTime.setLastOutBed(null);
        }
        sleepTime.setDeepSleepTime(deepSleepTime);
        sleepTime.setLightSleepTime(lightSleepTime);
        sleepTime.setAwakeTime(awakeTime);
        sleepTime.setOutBedTime(outBedTime);
        sleepTime.setTotalSleepTime(deepSleepTime + lightSleepTime);
        return sleepTime;
    }
}
